package de.justphil.tcg.tcgserver.game.helpers;

import static de.justphil.tcg.tcgserver.commons.logging.TCGLogging.*;

import org.slf4j.Logger;


import de.justphil.tcg.tcgserver.commons.exceptions.TCGException;
import de.justphil.tcg.tcgserver.commons.i18n.I18n;
import de.justphil.tcg.tcgserver.commons.i18n.M;
import de.justphil.tcg.tcgserver.commons.i18n.Error;
import de.justphil.tcg.tcgserver.game.GameManager;
import de.justphil.tcg.tcgserver.game.domain.Player;
import de.justphil.tcg.tcgserver.websockets.TCGWebSocket;
import de.justphil.tcg.tcgserver.websockets.interfaces.TCGAppManageable;
import de.justphil.tcg.tcgserver.commons.http.MessageResponseCode;
import de.justphil.tcg.tcgserver.websockets.messages.TCGMessage;
import de.justphil.tcg.tcgserver.websockets.util.MessageFactory;
import de.justphil.tcg.tcgserver.websockets.util.MessageWriter;

public class GameHelper {
	private static final Logger log = logger(GameHelper.class);
	
	public static void gracefullyDisconnectPlayer(GameManager game, TCGWebSocket socket) {
		Player p = game.getPlayerByWebSocket(socket);
		
		assert p != null : "Player p is null although it shouldn't be.";
		
		cleanUpAbortedPlayerConnection(game, p, socket);
		
		// broadcast that player left the game
		GameReporter.broadcastGameInfo(game, null, M.PLAYER_LEFT);
		
		// TODO graceful disconnect implies 'GameOver' -> handle GameOver
	}
	
	public static void cleanUpAbortedPlayerConnection(GameManager game, Player p, TCGWebSocket socket) {
		log.info("Cleaning up data associated with the disconnected player...");
		
		TCGAppManageable tcgApp = game.getTCGAppManageable();
		
		// remove corresponding entries in the HashMaps
		tcgApp.removePlayerIdToWebSocketMapping(p.getPlayerId());
		tcgApp.removeWebSocketToGameIdMapping(socket);
		
		// close WebSocket
		if (socket.isConnected())
			socket.close();
		
		// set player's WebSocket reference to null
		p.setSocket(null);
		
		log.info("Cleaning up data associated with the disconnected player...DONE!");
	}

	public static boolean registerConnectedPlayerAndNotifyAll(GameManager game, TCGWebSocket socket, TCGMessage msg) {
		// TODO Kill debug code
		log.debug("registerConnectedPlayerAndNotifyAll()");
		
		try {
			// TODO Kill debug code
			log.debug("playerId => {}", msg.getJoinFragment().getPlayerId());
			Player p = game.getPlayerById(msg.getJoinFragment().getPlayerId());
			
			if (p == null) {
				throw new TCGException(Error.CLIENT_ERROR_PLAYER_NOT_ALLOWED);
			}
			
			if (p.isConnected()) {
				throw new TCGException(Error.CLIENT_ERROR_PLAYER_ALREADY_CONNECTED);
			}
			
			if (!p.getOtp().equals(msg.getJoinFragment().getOtp())) {
				throw new TCGException(Error.CLIENT_ERROR_OTP_WRONG);
			}
			
			// ELSE
			
			// determine preferred locale if available
			p.setLocale( I18n.createLocale(msg.getLocale()) );
			
			// map connected socket to player
			p.setSocket(socket);
			
			TCGAppManageable tcgApp = game.getTCGAppManageable();
			
			// create mapping between player's websocket and game
			tcgApp.createWebSocketToGameIdMapping(socket, game.getGameId());
			
			// create mapping between playerId and player's websocket
			tcgApp.createPlayerIdToWebSocketMapping(p.getPlayerId(), socket);
			
			// tell the ConnectionMaintainer to maintain this in-game WebSocket
			//tcgApp.maintainInGameWebSocket(socket);
			
			// notify connected player and broadcast 'PLAYER_CONNECTED' info
			GameReporter.unicast(p, MessageWriter.writeJson(MessageFactory.joinAckMessage(MessageResponseCode.OK)));
			GameReporter.broadcastGameInfo(game, p, M.PLAYER_CONNECTED);
			
			return true;
		}
		catch (TCGException e) {
			socket.send(MessageWriter.writeJson(MessageFactory.errorMessage(e.getMessage())));
			return false;
		}
	}
	
	public static void commitGameChange(GameManager gameManager) {
		log.info("Commiting game change ({}) to cluster...", gameManager.getGameId());
		gameManager.getTCGAppManageable().commitGameChange(gameManager.getGameId(), gameManager);
		log.info("Commiting game change ({}) to cluster...DONE!", gameManager.getGameId());
		
		gameManager.getTCGAppManageable().printStatus();
	}
	
}
